package TangramCore;

import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;

public class HladinaObrys extends Hladina{
    Obrys.Validity ov;
    Dilek.Validity dv[];
    Rectangle2D rect = new Rectangle2D.Double(),tmp = new Rectangle2D.Double();
    int align;
    //private java.awt.geom.Point2D.Double pt = new java.awt.geom.Point2D.Double ();
    
    private boolean cleared = true, hidden = false, maxSize = false, difAdd/*,updated = false*/;
    
    Vzhled vzhledObrys, vzhledDilky;
    Paint paintBack;// = new Color(128, 0, 0);
    int vzhled;
    
    public HladinaObrys(Dilek d[], Obrys o, Vzhled vd, Vzhled vo, Paint bp, int a, boolean ms, Rectangle rp) {
        relpos = rp;
        //this.skladacka = o.skladacka;
        this.ov = new Obrys.Validity(o);
        this.dv = Dilek.Validity.getAll(d);
        //Arrays.sort(dv);
        vzhledObrys = vo;
        vzhledDilky = vd;
        difAdd = vzhledObrys.add != vzhledDilky.add;
        align = a;
        maxSize = ms;
        paintBack = bp;
        vzhled = VZHLED_OBRYS;
    }
    
    public void update(Dilek d[], Obrys o){
        //tmp.setRect(0,0,0,0);
        if(vzhled==VZHLED_OBRYS)
            addRect2D(tmp, ov.getLastValid());
        else
            for (int i = 0; i < dv.length; i++)
                addRect2D(tmp, dv[i].getLastValid());
        ov = new Obrys.Validity(o);
        dv = Dilek.Validity.getAll(d);
        //System.out.println("Update tmp: "+String.valueOf(tmp.getX())+" "+String.valueOf(tmp.getY())+" " + String.valueOf(tmp.getWidth())+" "+String.valueOf(tmp.getHeight()));
        //updated = true;
      /*ov.change(o);
    for(int i=0; i<dv.length;i++){
      dv[i].change(d[i]);
    }*/
    }
    
    public void resize(){
        double mer;
        Vzhled v = null;
        Rectangle2D r1 = null;
        
        if(vzhled==VZHLED_OBRYS){
            r1 = ov.o.getBounds2D();
            v = vzhledObrys;
        } else{
            v = vzhledDilky;
            r1 = dv[0].d.getBounds2D();
            for(int i = 1; i<dv.length; i++)
                r1.add(dv[i].d.getBounds2D());
        }
        
        Rectangle2D r2 = new Rectangle2D.Double(r1.getX()-v.add,r1.getY()-v.add,r1.getWidth()+2*v.add,r1.getHeight()+2*v.add);
        if(maxSize)
            mer = Math.min((double)pos.width / r2.getWidth(), (double)pos.height / r2.getHeight());
        else
            mer = (double) Math.min(pos.width, pos.height) / (Skladacka.MAXCOORD + 2*v.add);
        
        double x = pos.x + (pos.width - mer * r2.getWidth()) / 2 - mer * r2.getX();
        double y = pos.y + (pos.height - mer * r2.getHeight()) / 2 - mer * r2.getY();
        
        if(align == DEFAULT && !maxSize) {
            x = pos.x + (pos.width - mer * Skladacka.MAXCOORD) / 2;
            y = pos.y + (pos.height - mer * Skladacka.MAXCOORD) / 2;
        }
        if( (align & TOP) != 0)
            y =  pos.y - mer * r2.getY();
        else if( (align & BOTTOM) != 0)
            y = pos.y - mer * (r2.getY() + r2.getHeight()) + pos.height;
        if( (align & LEFT) != 0)
            x = pos.x - mer * r2.getX();
        else if( (align & RIGHT) != 0)
            x = pos.x - mer * (r2.getX() + r2.getWidth()) + pos.width;
        
        at = AffineTransform.getTranslateInstance(x,y);
        at.scale(mer, mer);
    }
    
    @Override
    public void resize(Rectangle r){
        super.resize(r);
        cleared = true;
        resize();
    }
    
    @Override    
    public void show() { //jen priznak, rect se nastavi az pri prvnim validate
        if(!hidden)
            return;
        cleared = true;
        hidden = false;
    }
    
    @Override    
    public void validate() {
        if (hidden)
            return;
        rect.setRect(0, 0, 0, 0);
        boolean zOrderChanged = false, tmpUsed = false;
        AffineTransform atpuv = at;
        Rectangle2D rectNew = new Rectangle2D.Double();//novy bounding rect
    /*if(!updated){
      tmp.setRect(0, 0, 0, 0); //tmp => puvodni bounding rect
      System.out.println("reseting tmp");
    }*/
        if(maxSize || align != DEFAULT){
            if(vzhled==VZHLED_OBRYS){
                addRect2D(tmp, ov.getLastValid());
                addRect2D(rectNew,ov.o.getBounds2D());
            } else{
                for (int i = 0; i < dv.length; i++){
                    addRect2D(tmp, dv[i].getLastValid());
                    addRect2D(rectNew,dv[i].d.getBounds2D());
                }
            }
            tmpUsed = true;
            if(!tmp.equals(rectNew))
                resize();
        }
        boolean change = !tmp.isEmpty() && (tmp.getWidth()!=rectNew.getWidth() || tmp.getHeight()!=rectNew.getHeight());
        if(change && (maxSize || align != DEFAULT)){
            addTransformedRect2D(obr.rectUpdate, tmp, atpuv);
            addTransformedRect2D(obr.rectUpdate, rectNew);
            
            if(vzhled==VZHLED_OBRYS)
                ov.validate(cleared,rect);
            else
                for (int i = 0; i < dv.length; i++)
                    if(dv[i].validate(cleared,rect)>Dilek.INV_VALID)
                        zOrderChanged=true;
            //System.out.println("rectNew: "+String.valueOf(rectNew.getX())+" "+String.valueOf(rectNew.getY())+" " + String.valueOf(rectNew.getWidth())+" "+String.valueOf(rectNew.getHeight()));
            //System.out.println("tmp: "+String.valueOf(tmp.getX())+" "+String.valueOf(tmp.getY())+" " + String.valueOf(tmp.getWidth())+" "+String.valueOf(tmp.getHeight()));
        } else {
            if(!tmpUsed)
                addRect2D(rect,tmp);
            if(vzhled == VZHLED_OBRYS){
                ov.validate(cleared, tmp);
                addRect2D(rect,tmp);
            } else{
                //if(dv[0].validate(cleared, rect)>Dilek.INV_VALID)
                //  zOrderChanged = true;
                for(int i=0;i<dv.length;i++){
                    if(dv[i].validate(cleared,tmp)>Dilek.INV_VALID)
                        zOrderChanged = true;
                    //rect.add(tmp);
                    addRect2D(rect,tmp);
                }
            }
            addTransformedRect2D(obr.rectUpdate,rect);
            //addRect(rectUpdate, at.createTransformedShape(rect).getBounds()); //transform
        }
        if(zOrderChanged){
            Arrays.sort(dv);
            //System.out.println("sorting Obrys");
        }
        cleared = false;
        //updated = false;
        tmp.setRect(0,0,0,0);
    }

    public void toggleVisibility(){
        if(hidden)
            show();
        else
            hide();
    }
    
    @Override    
    public void hide() {
        if (hidden)
            return;
        hidden = true;
        rect.setRect(0,0,0,0);
        if(paintBack == null){
            if(vzhled == VZHLED_OBRYS)
                addRect2D(rect, ov.getLastValid());
            else
                for(int i = 0; i<dv.length; i++)
                    addRect2D(rect,dv[i].getLastValid());
            addTransformedRect2D(obr.rectUpdate,rect);
        }
        else
            addRect2D(obr.rectUpdate,pos);
        //addRect(rectUpdate, at.createTransformedShape(rect).getBounds()); //transform
    }
    
    @Override
    public void draw(Graphics2D g) {
        if(hidden)
            return;
        AffineTransform old=g.getTransform();
        g.transform(at);
        if(vzhled == VZHLED_OBRYS){
            vzhledObrys.plotShape(g, ov.o);
      /*for(int i=0; i<ov.o.od.pTmp.length; i++){ //debug
        ca.uoguelph.squire.Point2D p = ov.o.od.pTmp[i];
        if(p!= null)
          g.fillOval((int)(p.x-0.5),(int)(p.y-0.5),(int)(p.x+1),(int)(p.y+1));
      }*/
        } else{
            for (int i = 0; i < dv.length; i++)
                vzhledDilky.plotShape(g, dv[i].d);
        }
        g.setTransform(old);
    }
    
    @Override
    public void clear(Graphics2D g) {
        if(paintBack != null){
            g.setPaint(paintBack);
            g.fill(pos);
        }
    }
    
    public void prepniVzhled(){
        boolean shown = !hidden;
        if(shown)
            hide();
        vzhled = (vzhled == VZHLED_OBRYS) ? VZHLED_DILKY : VZHLED_OBRYS;
        if (difAdd)
            resize(); //kvuli rozdilu v.add
        if(shown)
            show();
    }
    
    public void prepniVzhled(int v){
        if(v == vzhled)
            return;
        boolean shown = !hidden;
        if(shown)
            hide();
        vzhled = v;
        resize(); //kvuli rozdilu v.add
        if(shown)
            show();
    }
    
    public void setVzhled(Vzhled v){
        if(vzhled == VZHLED_DILKY)
            setVzhled(v, null);
        else
            setVzhled(null, v);
    }
    public void setVzhled(Vzhled vd, Vzhled vo){
        boolean shown = !hidden;
        if(shown)
            hide();
        vzhledObrys = vo == null ? vzhledObrys : vo;
        vzhledDilky = vd == null ? vzhledDilky : vd;
        resize();
        if(shown)
            show();
    }
    
    public Vzhled getVzhled(){
        return vzhled == VZHLED_DILKY ? vzhledDilky : vzhledObrys;
    }
    
    @Override
    public boolean contains(Point p){
        if(hidden)
            return false;
        Point2D pp = new Point2D(p.x, p.y);
        try {
        at.inverseTransform(pp, pp);
        }catch(Exception ex){}
        return ov.o.contains(pp);
    }
    public Dilek[] getDilky(){
        Dilek[] ret = new Dilek[dv.length];
        for(int i = 0; i< dv.length; i++)
           ret[i] = dv[i].d;
        return ret;
    }
    public Obrys getObrys(){
        return ov.o;
    }
}
